/*
 * Copyright (c) 2022, IMMORTA Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 * - Redistributions of source code must retain the above copyright notice, this list
 *   of conditions and the following disclaimer.
 *
 * - Redistributions in binary form must reproduce the above copyright notice, this
 *   list of conditions and the following disclaimer in the documentation and/or
 *   other materials provided with the distribution.
 *
 * - Neither the name of IMMORTA Inc. nor the names of its
 *   contributors may be used to endorse or promote products derived from this
 *   software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/*!
 * @file os_adapter_baremetal.c
 * @brief This file defines baremetal os_adapter function
 */

/*******Includes***************************************************************/
#include "os_adapter.h"
#include "clock_drv.h"

#if defined USING_OS_FREERTOS
#error "Wrong os_adapter selected. Please define symbol USING_OS_BAREMETAL (or no OS define) in project settings or change the os_adapter variant"
#elif defined USING_OS_UCOS
#error "Wrong os_adapter selected. Please define symbol USING_OS_BAREMETAL (or no OS define) in project settings or change the os_adapter variant"
#endif
/*******Definitions************************************************************/
#define OS_ADAPTER_TICKS_PER_MSEC        (1U)

/*! @brief Converts milliseconds to ticks - in this case, one tick = one millisecond */
#define MSEC_TO_TICK(msec)      ((msec) * (OS_ADAPTER_TICKS_PER_MSEC))
#define TICK_TO_MSEC(tick)      ((tick) / (OS_ADAPTER_TICKS_PER_MSEC))
#define SysTick_RELOAD_Msk      (0xFFFFFFU)
#define SysTick_RELOAD_Pos      (0U)

#define SysTick_RELOAD(x)       (((uint32_t)(((uint32_t)(x))<<SysTick_RELOAD_Pos))&SysTick_RELOAD_Msk)


/*******Variables**************************************************************/
static volatile uint32_t s_osAdapterTickCnt = 0U;
static bool first_InitSystick = true;
static os_adapter_tick_callback_t s_tickCallback = NULL;
static void* s_callbackParam = NULL;

/*******Prototypes*************************************************************/
void SysTick_Handler(void);

/*******Private Functions******************************************************/
static inline uint32_t OS_Adapter_GetCurrentTickCount(void);
static inline void OS_Adapter_Tick(void);
static inline void OS_Adapter_UpdateTickConfig(void);

/*******Code*******************************************************************/

/*******************************************************************************
 *** Function Name : SysTick_Handler
 *** Description   : System tick handler
 ******************************************************************************/
void SysTick_Handler(void)
{
    OS_Adapter_Tick();
		
    if (s_tickCallback != NULL) {
        s_tickCallback(s_callbackParam);
    }
}

/*******************************************************************************
 *** Function Name : OS_Adapter_Init
 *** Description   : Initialize system tick clock
 ******************************************************************************/
void OS_Adapter_Init(os_adapter_baremetal_config_t* config)
{
    if (config != NULL) {
        s_tickCallback = config->tickCallback;
        s_callbackParam = config->callbackParam;
    }

    OS_Adapter_UpdateTickConfig();
}

/*******************************************************************************
 *** Function Name : OS_Adapter_DelayTime
 *** Description   : Delays execution for a number of milliseconds(ticks)
 ******************************************************************************/
void OS_Adapter_DelayTime(os_adapter_time_unit_t mode, uint32_t delay)
{
    OS_Adapter_UpdateTickConfig();

    uint32_t start = OS_Adapter_GetCurrentTickCount();
    uint32_t crt_ticks = OS_Adapter_GetCurrentTickCount();
    uint32_t delta = crt_ticks - start;
    uint32_t delay_ticks = 0;

    if (mode == OS_ADAPTER_TIME_UNIT_MS) {
        delay_ticks = MSEC_TO_TICK(delay);
    } else {
        delay_ticks = delay;
    }

    while (delta < delay_ticks) {
        crt_ticks = OS_Adapter_GetCurrentTickCount();
        delta = crt_ticks - start;
    }
}

/*******************************************************************************
 *** Function Name : OS_Adapter_GetTimeElapsed
 *** Description   : Returns the number of miliseconds(ticks) elapsed
 ******************************************************************************/
uint32_t OS_Adapter_GetTimeElapsed(os_adapter_time_unit_t mode)
{
    uint32_t elapsedTime;

    if (mode == OS_ADAPTER_TIME_UNIT_MS) {
        elapsedTime = TICK_TO_MSEC(OS_Adapter_GetCurrentTickCount());
    } else {
        elapsedTime = OS_Adapter_GetCurrentTickCount();
    }

    return elapsedTime;
}

/*******************************************************************************
 *** Function Name : OS_Adapter_CreateSemaphore
 *** Description   : Creates a semaphore with a given value
 ******************************************************************************/
errcode_t OS_Adapter_CreateSemaphore(semaphore_t* semaphore, uint8_t initValue)
{
    ASSERT_PARAM(semaphore != NULL);

    volatile uint32_t mask;

    mask = System_SuspendAllInterrupts();
    *semaphore = initValue;
    System_ResumeAllInterrupts(mask);

    return ERR_SUCCESS;
}

/*******************************************************************************
 *** Function Name : OS_Adapter_PostSemaphore
 *** Description   : Increment a semaphore
 ******************************************************************************/
errcode_t OS_Adapter_PostSemaphore(semaphore_t* semaphore)
{
    ASSERT_PARAM(semaphore != NULL);

    errcode_t errCode = ERR_SUCCESS;
    volatile uint32_t mask;

    mask = System_SuspendAllInterrupts();
    if (*semaphore != 255U) {
        (*semaphore)++;
    } else {
        errCode = ERR_ERROR;
    }
    System_ResumeAllInterrupts(mask);

    return errCode;
}

/*******************************************************************************
 *** Function Name : OS_Adapter_WaitSemaphore
 *** Description   : Decrement a semaphore with timeout.
 ******************************************************************************/
errcode_t OS_Adapter_WaitSemaphore(semaphore_t* semaphore, uint32_t timeout)
{
    ASSERT_PARAM(semaphore != NULL);

    errcode_t errCode = ERR_SUCCESS;

    uint32_t timeoutTicks;
    uint32_t start;
    uint32_t end;
    uint32_t max;
    uint32_t crt_ticks;
    uint32_t delta;
    volatile uint32_t mask;

    OS_Adapter_UpdateTickConfig();
    if (timeout == 0u) {
        /* when the timeout is 0 the wait operation is the equivalent of try_wait,
            meaning that if the semaphore is 0 return immediately with an error code
        */
        if (*semaphore == 0u) {
            errCode = ERR_TIMEOUT;
        }
    } else {
        /* timeout is not 0 */

        if (timeout == OS_ADAPTER_WAIT_FOREVER) {
            timeoutTicks = OS_ADAPTER_WAIT_FOREVER;
        } else {
            /* Convert timeout from milliseconds to ticks. */
            timeoutTicks = MSEC_TO_TICK(timeout);
        }
        start = OS_Adapter_GetCurrentTickCount();
        end = (uint32_t)(start + timeoutTicks);
        max = end - start;
        while (*semaphore == 0u) {
            crt_ticks = OS_Adapter_GetCurrentTickCount();
            delta = crt_ticks - start;
            if ((timeoutTicks != OS_ADAPTER_WAIT_FOREVER) && (delta > max)) {
                /* Timeout occured, stop waiting and return fail code */
                errCode = ERR_TIMEOUT;
                break;
            }
        }
    }

    if (errCode == ERR_SUCCESS) {
        mask = System_SuspendAllInterrupts();
        (*semaphore)--;
        System_ResumeAllInterrupts(mask);
    }

    return errCode;
}

/*******************************************************************************
 *** Function Name : OS_Adapter_DestroySemaphore
 *** Description   : Destroys a previously created mutex
 ******************************************************************************/
errcode_t OS_Adapter_DestroySemaphore(semaphore_t* semaphore)
{
    ASSERT_PARAM(semaphore != NULL);

    (void)semaphore;

    return ERR_SUCCESS;
}

/*******************************************************************************
 *** Function Name : OS_Adapter_CreateMutex
 *** Description   : Create an unlocked mutex
 ******************************************************************************/
errcode_t OS_Adapter_CreateMutex(mutex_t* mutex)
{
    (void)mutex;

    return ERR_SUCCESS;
}

/*******************************************************************************
 *** Function Name : OS_Adapter_UnlockMutex
 *** Description   : Unlocks a previously locked mutex
 ******************************************************************************/
errcode_t OS_Adapter_UnlockMutex(mutex_t* mutex)
{
    (void)mutex;

    return ERR_SUCCESS;
}

/*******************************************************************************
 *** Function Name : OS_Adapter_LockMutex
 *** Description   : Waits for a mutex and locks it
 ******************************************************************************/
errcode_t OS_Adapter_LockMutex(mutex_t* mutex, uint32_t timeout)
{
    (void)mutex;

    return ERR_SUCCESS;
}

/*******************************************************************************
 *** Function Name : OS_Adapter_DestroyMutex
 *** Description   : Destroys a previously created mutex
 ******************************************************************************/
errcode_t OS_Adapter_DestroyMutex(mutex_t* mutex)
{
    (void)mutex;

    return ERR_SUCCESS;
}

/*******************************************************************************
 *** Function Name : OS_Adapter_Deinit
 *** Description   : Deinitialize system tick clock
 ******************************************************************************/
void OS_Adapter_Deinit(void)
{
    SysTick->CTRL = 0x4U;
    SysTick->LOAD = 0xFFFFFFU;
    SysTick->VAL = 0xFFFFFFU;

    s_tickCallback = NULL;
    s_callbackParam = NULL;
    s_osAdapterTickCnt = 0U;
    first_InitSystick = true;
}

/*******************************************************************************
 *** Function Name : OS_Adapter_GetCurrentTickCount
 *** Description   : Get current tick count
 ******************************************************************************/
static inline uint32_t OS_Adapter_GetCurrentTickCount(void)
{
    return s_osAdapterTickCnt;
}

/**
 * yhy define 
*/
uint32_t OS_Adapter_GetCurrentTick(void)
{
	return s_osAdapterTickCnt;
}

/*******************************************************************************
 *** Function Name : OS_Adapter_Tick
 *** Description   : Os_Adapter tick count
 ******************************************************************************/
static inline void OS_Adapter_Tick(void)
{
    s_osAdapterTickCnt++;
}


/*******************************************************************************
 *** Function Name : OS_Adapter_UpdateTickConfig
 *** Description   : Update system tick configuration
 ******************************************************************************/
static inline void OS_Adapter_UpdateTickConfig(void)
{
    uint32_t core_freq = 0U;
    clk_names_t coreclk = CLK_SYS;

    if (first_InitSystick) {
        /* Get the correct name of the core clock */
        (void)CLOCK_GetFreq(coreclk, &core_freq);
        ASSERT_PARAM(core_freq > 0U);

        SysTick->CTRL = SysTick->CTRL & 0xFFFFFFFEU;
        SysTick->LOAD = SysTick_RELOAD(core_freq / 1000U);

        /* only initialize CVR on the first entry, to not cause time drift */
        SysTick->VAL = 0U;
        SysTick->CTRL = 0x7U;
        first_InitSystick = false;
    }
}

/*******EOF********************************************************************/
